mod token {
    #[derive(Debug, Clone, PartialEq)]
    pub enum TokenKind {
        Number(f64),
        Plus,
        Minus,
        Multiply,
        Divide,
        LParen,
        RParen,
        Eof,
    }

    #[derive(Debug, PartialEq)]
    pub struct Token {
        pub kind: TokenKind,
        pub lexeme: String,
    }

    impl Token {
        pub fn new(kind: TokenKind, lexeme: &str) -> Self {
            Self {
                kind,
                lexeme: lexeme.to_string(),
            }
        }
    }
}

mod lexer {
    use crate::token::{Token, TokenKind};

    pub struct Lexer {
        input: Vec<char>,
        pos: usize,
    }

    impl Lexer {
        pub fn new(input: &str) -> Self {
            Self {
                input: input.chars().collect(),
                pos: 0,
            }
        }

        pub fn next_token(&mut self) -> Token {
            self.skip_whitespace();

            if self.pos >= self.input.len() {
                return Token::new(TokenKind::Eof, "\0");
            }

            let current_char = self.input[self.pos];

            match current_char {
                '+' => {
                    self.pos += 1;
                    Token::new(TokenKind::Plus, "+")
                }
                '-' => {
                    self.pos += 1;
                    Token::new(TokenKind::Minus, "-")
                }
                '*' => {
                    self.pos += 1;
                    Token::new(TokenKind::Multiply, "*")
                }
                '/' => {
                    self.pos += 1;
                    Token::new(TokenKind::Divide, "/")
                }
                '(' => {
                    self.pos += 1;
                    Token::new(TokenKind::LParen, "(")
                }
                ')' => {
                    self.pos += 1;
                    Token::new(TokenKind::RParen, ")")
                }
                _ => {
                    if current_char.is_ascii_digit() {
                        // let mut num = 0;
                        // while self.pos < self.input.len() && self.input[self.pos].is_ascii_digit() {
                        //     num = num * 10 + (self.input[self.pos] as i64 - '0' as i64);
                        //     self.pos += 1;
                        // }
                        // Token::new(TokenKind::Number(num), &num.to_string())

                        self.eat_number()
                    } else {
                        panic!("Unexpected character: {}", current_char);
                    }
                }
            }
        }

        fn skip_whitespace(&mut self) {
            while self.pos < self.input.len() && self.input[self.pos].is_whitespace() {
                self.pos += 1;
            }
        }

        fn eat_number(&mut self) -> Token {
            let mut number_string = String::from("");

            // while self.pos < self.input.len() && self.input[self.pos].is_ascii_digit() {
            //     number_string.push(self.input[self.pos]);
            //     self.pos += 1;
            // }
            //
            // if self.pos < self.input.len()
            //     && self.input[self.pos] == '.'
            //     && self.input[self.pos + 1].is_ascii_digit()
            // {
            //     number_string.push('.');
            //     self.pos += 1;
            // }
            //
            // while self.pos < self.input.len() && self.input[self.pos].is_ascii_digit() {
            //     number_string.push(self.input[self.pos]);
            //     self.pos += 1;
            // }

            while (self.pos < self.input.len() && self.input[self.pos].is_ascii_digit())
                || (self.pos < self.input.len()
                    && self.input[self.pos] == '.'
                    && self.input[self.pos + 1].is_ascii_digit())
            {
                number_string.push(self.input[self.pos]);
                self.pos += 1;
            }

            if self.pos < self.input.len()
                && (self.input[self.pos] == 'e' || self.input[self.pos] == 'E')
            {
                number_string.push(self.input[self.pos]);
                self.pos += 1;

                if self.input[self.pos] == '+' || self.input[self.pos] == '-' {
                    number_string.push(self.input[self.pos]);
                    self.pos += 1;
                }
                while self.pos < self.input.len() && self.input[self.pos].is_ascii_digit() {
                    number_string.push(self.input[self.pos]);
                    self.pos += 1;
                }
            }

            Token::new(
                TokenKind::Number(number_string.parse().unwrap()),
                &number_string,
            )
        }
    }
}

mod parser {
    use crate::lexer::Lexer;
    use crate::token::{Token, TokenKind};

    #[derive(Debug)]
    pub enum Expr {
        Number(f64),
        BinOp {
            left: Box<Expr>,
            op: Op,
            right: Box<Expr>,
        },
    }

    #[derive(Debug)]
    pub enum Op {
        Plus,
        Minus,
        Multiply,
        Divide,
    }

    pub struct Parser {
        lexer: Lexer,
        current_token: Token,
    }

    impl Parser {
        pub fn new(mut lexer: Lexer) -> Self {
            let current_token = lexer.next_token();
            Self {
                lexer,
                current_token,
            }
        }

        fn eat(&mut self, kind: TokenKind) {
            if self.current_token.kind == kind {
                self.current_token = self.lexer.next_token();
            } else {
                panic!("Unexpected token: {:?}", self.current_token);
            }
        }

        pub fn parse(&mut self) -> Expr {
            self.parse_expr()
        }

        fn parse_expr(&mut self) -> Expr {
            let mut node = self.parse_term();

            while self.current_token.kind == TokenKind::Plus
                || self.current_token.kind == TokenKind::Minus
            {
                let op = match self.current_token.kind {
                    TokenKind::Plus => Op::Plus,
                    TokenKind::Minus => Op::Minus,
                    _ => unreachable!(),
                };
                self.eat(self.current_token.kind.clone());
                node = Expr::BinOp {
                    left: Box::new(node),
                    op,
                    right: Box::new(self.parse_term()),
                }
            }

            node
        }

        fn parse_term(&mut self) -> Expr {
            let mut node = self.parse_factor();

            while self.current_token.kind == TokenKind::Multiply
                || self.current_token.kind == TokenKind::Divide
            {
                let op = match self.current_token.kind {
                    TokenKind::Multiply => Op::Multiply,
                    TokenKind::Divide => Op::Divide,
                    _ => unreachable!(),
                };
                self.eat(self.current_token.kind.clone());
                node = Expr::BinOp {
                    left: Box::new(node),
                    op,
                    right: Box::new(self.parse_factor()),
                }
            }

            node
        }

        fn parse_factor(&mut self) -> Expr {
            match self.current_token.kind {
                TokenKind::Number(n) => {
                    self.eat(TokenKind::Number(n));
                    Expr::Number(n)
                }
                TokenKind::LParen => {
                    self.eat(TokenKind::LParen);
                    let node = self.parse_expr();
                    self.eat(TokenKind::RParen);

                    node
                }
                _ => panic!("Unexpected token: {:?}", self.current_token),
            }
        }
    }
}

mod evaluate {
    use crate::parser::{Expr, Op};

    pub fn evaluate(expr: &Expr) -> f64 {
        match expr {
            Expr::Number(n) => *n,
            Expr::BinOp { left, op, right } => {
                let left_value = evaluate(left);
                let right_value = evaluate(right);
                match op {
                    Op::Plus => left_value + right_value,
                    Op::Minus => left_value - right_value,
                    Op::Multiply => left_value * right_value,
                    Op::Divide => left_value / right_value,
                }
            }
        }
    }
}

fn main() {
    let input = "3.2 + 5 * (10 - 4) / 24e-1";
    // let input = "24E-1";
    println!("{input}");

    let lexer = lexer::Lexer::new(input);
    // loop {
    //     let token = lexer.next_token();
    //     if token.kind == TokenKind::Eof {
    //         break;
    //     }
    //     println!("{token:?}");
    // }
    let mut parser = parser::Parser::new(lexer);
    let ast = parser.parse();
    let result = evaluate::evaluate(&ast);
    println!("{}", result);
}
